package com.example.java_gobang.api.controller;

import com.example.java_gobang.game.service.OnlineUserManager;
import com.example.java_gobang.game.vo.GameReadyResponse;
import com.example.java_gobang.game.model.Room;
import com.example.java_gobang.game.service.RoomManager;
import com.example.java_gobang.game.vo.GameResponse;
import com.example.java_gobang.model.User;
import com.example.java_gobang.model.UserMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.io.IOException;

@Slf4j
@Component
public class GameAPI extends TextWebSocketHandler {
    private final ObjectMapper mapper = new ObjectMapper();

    @Autowired
    private RoomManager roomManager;

    @Autowired
    private OnlineUserManager onlineUserManager;

    @Resource
    private UserMapper userMapper;


    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        GameReadyResponse response = new GameReadyResponse();
        // 1.获取用户信息
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            response.setOk(false);
            response.setReason("用户尚未登录！");
            session.sendMessage(new TextMessage(mapper.writeValueAsString(response)));
            session.close(CloseStatus.SERVER_ERROR);
            return;
        }

        // 2.判断当前用户是否在房间里
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if (room == null) {
            response.setOk(false);
            response.setReason("当前用户尚未匹配到！");
            session.sendMessage(new TextMessage(mapper.writeValueAsString(response)));
            session.close();
            return;
        }

        // 3.判断用户多开
        // 判断是否同时在游戏大厅和游戏房间
        if (onlineUserManager.getFromGameHall(user.getUserId()) != null || onlineUserManager.getFromGameRoom(user.getUserId()) != null) {
            response.setOk(true);
            response.setReason("当前用户已经在其他游戏房间内！");
            response.setMessage("repeatConnection");
            session.sendMessage(new TextMessage(mapper.writeValueAsString(response)));
            session.close();
            return;
        }

        // 4.设置当前玩家上线
        onlineUserManager.enterGameRoom(user.getUserId(), session);

        // 5.把玩家加入到游戏房间中
        synchronized (room) {
            if (room.addUser(user)) {
                // 唤醒等待的线程
                room.notifyAll();

                // 等待房间准备好
                while (room.getUser1() == null || room.getUser2() == null) {
                    room.wait();
                }

                // 通知玩家游戏准备就绪
                noticeGameReady(room, user);
                log.info("玩家: {} 准备就绪", user.getUserId());
            } else {
                response.setOk(false);
                response.setMessage("房间已满，加入房间失败！");
            }
        }
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 1.获取用户身份信息
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            log.info("玩家: {} 尚未登录", user.getUserId());
            return;
        }
         // 2.获取玩家的房间
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if (room == null) {
            log.warn("当前用户 {} 游戏房间不存在!", user.getUsername());
            return;
        }

        // 3.计算落子操作结果
        room.putChess(message.getPayload());
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            // 此处就简单处理, 在断开连接的时候就不给客户端返回响应了.
            return;
        }
        WebSocketSession exitSession = onlineUserManager.getFromGameRoom(user.getUserId());
        if (session == exitSession) {
            // 加上这个判定, 目的是为了避免在多开的情况下, 第二个用户退出连接动作, 导致第一个用户的会话被删除.
            onlineUserManager.exitGameRoom(user.getUserId());
        }
        log.warn("当前用户 {} 游戏房间连接异常!", user.getUsername());

        // 通知对手获胜
        noticeThatUserWin(user);
    }


    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            // 此处就简单处理, 在断开连接的时候就不给客户端返回响应了.
            return;
        }
        WebSocketSession exitSession = onlineUserManager.getFromGameRoom(user.getUserId());
        if (session == exitSession) {
            // 加上这个判定, 目的是为了避免在多开的情况下, 第二个用户退出连接动作, 导致第一个用户的会话被删除.
            onlineUserManager.exitGameRoom(user.getUserId());
        }
        log.warn("当前用户 {} 离开游戏房间!", user.getUsername());

        // 通知对手获胜
        noticeThatUserWin(user);
    }


    private void noticeGameReady(Room room, User thisUser) throws IOException {
        // 创建游戏准备响应对象
        GameReadyResponse resp = new GameReadyResponse();
        resp.setMessage("gameReady");
        resp.setOk(true);
        resp.setReason("");
        resp.setRoomId(room.getRoomId());
        resp.setThisUserId(thisUser.getUserId());

        // 判断当前用户是 user1 还是 user2，并获取对手用户
        User thatUser;
        if (room.getUser1().equals(thisUser)) {
            thatUser = room.getUser2();
        } else {
            thatUser = room.getUser1();
        }
        resp.setThatUserId(thatUser.getUserId());
        resp.setWhiteUserId(room.getWhiteUser());

        // 获取当前用户的 WebSocket 会话
        WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thisUser.getUserId());
        // 将响应对象转换为 JSON 并通过 WebSocket 发送给用户
        webSocketSession.sendMessage(new TextMessage(mapper.writeValueAsString(resp)));
    }

    private void noticeThatUserWin(User user) throws IOException {
        // 1.找到当前玩家房间
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if (room == null) {
            log.info("房间已经解散，无需通知对方");
            return;
        }

        // 2.根据房间找到对手
        User thatUser = user == room.getUser1() ? room.getUser2() : room.getUser1();

        // 3.找到对手的在线状态
        WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thatUser.getUserId());
        if (webSocketSession == null) {
            log.info("对方也掉线了");
            return;
        }

        // 4.构造响应通知对手
        GameResponse resp = new GameResponse();
        resp.setMessage("putChess");
        resp.setUserId(thatUser.getUserId());
        resp.setWinner(thatUser.getUserId());
        webSocketSession.sendMessage(new TextMessage(mapper.writeValueAsString(resp)));

        // 5.更新玩家分数
        int winUserId = thatUser.getUserId();
        int loseUserId = user.getUserId();
        userMapper.userWin(winUserId);
        userMapper.userLose(loseUserId);

        // 6.释放房间对象
        roomManager.remove(room, room.getUser1().getUserId(),room.getUser2().getUserId());
    }
}
